package com.zoran.study.spring.validation.validator;


import com.zoran.study.spring.validation.constraint.IdentityNumber;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.Arrays;
import java.util.Calendar;
import java.util.HashSet;
import java.util.Set;

public class IdentityNumberValidator implements ConstraintValidator<IdentityNumber, String> {

    private final Set<Integer> zones = new HashSet<>(
            Arrays.asList(11, 12, 13, 14, 15, 21, 22, 23, 31, 32, 33, 34, 35, 36, 37, 41, 42, 43,
                    44, 45, 46, 50, 51, 52, 53, 54, 61, 62, 63, 64, 65, 71, 81, 82, 91));

    private final static int[] PARITYBIT = { '1', '0', 'X', '9', '8', '7', '6', '5', '4', '3',
            '2' };

    private final static int[] POWER_LIST = { 7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2 };

    @Override
    public void initialize(IdentityNumber constraintAnnotation) {
    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        if (value == null || (value.length() != 15 && value.length() != 18)) {
            return false;
        }

        final char[] cs = value.toUpperCase().toCharArray();
        int power = 0;
        for (int i = 0; i < cs.length; i++) {
            if (i == cs.length - 1 && cs[i] == 'X') {
                break;
            }

            if (cs[i] < '0' || cs[i] > '9') {
                return false;
            }

            if (i < cs.length - 1) {
                power += (cs[i] - '0') * POWER_LIST[i];
            }
        }

        if (!zones.contains(Integer.valueOf(value.substring(0, 2)))) {
            return false;
        }

        String year = value.length() == 15 ? "19" + value.substring(6, 8) : value.substring(6, 10);
        final int iyear = Integer.parseInt(year);
        if (iyear < 1900 || iyear > Calendar.getInstance().get(Calendar.YEAR)) {
            return false;
        }

        String month = value.length() == 15 ? value.substring(8, 10) : value.substring(10, 12);
        final int imonth = Integer.parseInt(month);
        if (imonth < 1 || imonth > 12) {
            return false;
        }

        String day = value.length() == 15 ? value.substring(10, 12) : value.substring(12, 14);
        final int iday = Integer.parseInt(day);
        if (iday < 1 || iday > 31) {
            return false;
        }

        if (value.length() == 15) {
            return true;
        }

        return cs[cs.length - 1] == PARITYBIT[power % 11];
    }

}
